package controller

import (
	comm "../Common"
	db "../dataBase"
	"../model"
	modl "../model"
	pb "../protobuf"
	"fmt"
	"golang.org/x/net/context"
	"gopkg.in/mgo.v2/bson"
	"runtime"
	"strconv"
	"sync"
	"time"
	use"../service/user"
)

type  quesNum struct {
	all int;
	new_easy int;
	new_middle int;
	new_diff int;
	worong_easy int;
	worong_middle int;
	worong_diff int;
	review_easy int;
	review_middle int;
	review_diff int;
}
//存储临时知识点
type point struct {
	id string;
	state int32;//1回顾,2,错题,3新知识
}
type pointsall  struct{
	err []string;
	rev []string;
	new []string;
}
type  ques struct {
	new_easy []Model.Questions;
	new_middle []Model.Questions;
	new_diff []Model.Questions;
	worong_easy []Model.Questions;
	worong_middle []Model.Questions;
	worong_diff []Model.Questions;
	review_easy []Model.Questions;
	review_middle []Model.Questions;
	review_diff []Model.Questions;
}



var goCount=0//记录当前协程数量

func init() {
	//启动工厂
	go workePool()
}
var allcount=0
func workePool()  {
	fmt.Println("工厂任务队列启动")
	cpuCount:=runtime.NumCPU()//获取当前cpu核数
	runtime.GOMAXPROCS(cpuCount)//启用cpu多核处理，注意，IO密集型操作启用多核并不会带来性能提高反而影响性能
	allCount:=cpuCount*2//设置最大线程数量
	fmt.Printf("Cpu核心数:%d\n最大线程:%d\n",cpuCount,allCount)
	var wg sync.WaitGroup
	for true {

		timer1 := time.NewTimer(300)//每200毫秒获取一次任务
		<-timer1.C
		var dat=[]modl.TestpaperQueue{}
		_=db.FindTespaper("db","TestpaperQueue",bson.M{"state":0},nil,&dat,allCount-goCount,goCount)
		for _,v:= range dat{
			//fmt.Printf("创建线程，当前生成试卷线程数:%d,服务总线程数:%d\n",goCount,runtime.NumGoroutine())
			go Testpaper(&wg,v)
			goCount++
			wg.Add(1)
	}
	}
}

//任务发布者（对外数据接口）
func (s Service) CreateTestpaper(ctx context.Context, in *pb.CreateTestpaperRequest) (*pb.CreateTestpaperReply,error) {
	a,_:=use.ValidateToken(in.Userid)
	if(a.Error){
		return &pb.CreateTestpaperReply{Error:true,ErrorMsg:"Token有误解析失败",State:1,Queue:0.,Question:nil},nil
	}
	in.Userid=a.Result
	var dat=modl.TestpaperQueue{}
	//fmt.Println("调用接口")
   _=db.FindOne("db","TestpaperQueue",bson.M{"userid":in.Userid},nil,&dat)
	//如果没有此任务
   if(dat.Userid==""){
	   //fmt.Print("调用插入")
	   dat.Addtime=time.Now().UnixNano()/1e6
	   dat.Questions=nil
	   dat.State=0
	   dat.Role=*in.Role
	   dat.Userid=in.Userid
	   dat.CreateTestpaperID=in.CreateTestpaperID
	   dat.Courseid=in.Courseid
		db.Inserts("db","TestpaperQueue",dat)
	}
   //如果此任务已完成
   if(dat.State==1){
   	db.Remove("db","TestpaperQueue",bson.M{"userid":in.Userid})
	   allcount++

	   if paperToMysql(dat){
		   //fmt.Printf("返回试卷，当前生成试卷总量:%d\n",allcount)
		   return &pb.CreateTestpaperReply{Error:false,ErrorMsg:"",State:1,Queue:0.,Question:dat.Questions},nil
	   }else {
		   //fmt.Printf("反悔失败，入库失败\n")
	   	return &pb.CreateTestpaperReply{Error:true,ErrorMsg:"试卷存入mysql数据库失败",State:1,Queue:0.,Question:nil},nil
	   }

	}
   //如果此任务未处理
	//var dat2=modl.TestpaperQueue{}
	queue,_:=db.Count("db","TestpaperQueue",bson.M{"addTime":bson.M{"$lt":dat.Addtime},"state":0})
	return &pb.CreateTestpaperReply{Error:false,ErrorMsg:"",State:0,Queue:int32(queue),Question:nil},nil
}

func paperToMysql(dat modl.TestpaperQueue)bool{
	var tesid=comm.GetGuid()
	//fmt.Printf("id%v",tesid)
	var str="INSERT INTO `testpaper` (Testpaperid,UserID,TestpaperRuleID,CreateTestpaperID,BeginTime,Type,QuestionsNum) VALUES ( '"
	str+= tesid
	str+="','"
	str+=dat.Userid
	str+="','"
	str+=dat.TestpaperRoleID
	str+="','"
	str+=dat.CreateTestpaperID
	str+="',"
	str+=strconv.Itoa(int(time.Now().Unix()))
	str+=","
	str+=strconv.Itoa(int(dat.Type))
	str+=","
	str+=strconv.Itoa(len(dat.Questions))
	str+=")"
	var str2="INSERT INTO 	`testpaperquestions` (TestpaperQuestionsID,TestpaperID,QuestionsID,CourseID,UserID,Sort,Type ) VALUES "
	for i,v:=range dat.Questions {
		str2+="('";str2+=comm.GetGuid();str2+="','";str2+=tesid;str2+="','";str2+=v.QuestionsID
		str2+="','";str2+=dat.Courseid;str2+="','";str2+=dat.Userid;str2+="',";str2+=strconv.Itoa(i+1);str2+=",";str2+=strconv.Itoa(int(dat.Type))
		str2+="),"
	}
	str2= string([]rune(str2)[:len(str2)-1])
	//fmt.Printf("%v\n",str2)
	//var str2="INSERT INTO `ceshi` (ceshu) VALUES "
	mydb,_:=db.GetMysqlDb()
	//_,err:=mydb.Exec(str)
	conn, err := mydb.Begin()
	if err != nil {
		return false
	}

	//插入数据入表
	insert_stmt, err := conn.Prepare(str)
	if err != nil {
		fmt.Println("exec failedtestpaper,", err)
		return false
	}
	_,err= insert_stmt.Exec()
	//id,_ := insert_res.LastInsertId()

	//_,err= r.LastInsertId()
	if err != nil {
		fmt.Println("exec failedtestpaper2,", err)
		//回滚
		conn.Rollback()
		return false
	}
	insert_stmt2, err:= conn.Prepare(str2)
	if err != nil {
		fmt.Println("exec failedquestion,", err)
		conn.Rollback()
		return false
	}
	_,err= insert_stmt2.Exec()
	//id,err:= insert_stmt2.LastInsertId()
	if err != nil {
		fmt.Println("exec failedquestion2,", err)
		//回滚
		conn.Rollback()
		return false
	}
	//fmt.Println("insert succ:", id)
	//提交事务
	conn.Commit()
	return true
}

func  Testpaper(wg *sync.WaitGroup, in modl.TestpaperQueue) {

	// 接口实现
	//fmt.Printf("开始:%v\n",in)
	//var t=time.Now().UnixNano()/1e6
	 ret:=[]*pb.Questions{}
	var c=in.Role
	//获取到九个不同等级的试题数量
	numcha:=make(chan quesNum)
	go getQuestionNum(&c,numcha)
	//先拿该试卷所用到的所有知识点
	var pointall=make(map[string] point)
	var r []string
	var w []string
	var n []string
	var errpoints=[]Model.Userknowledgepoint{}
	db.FindAll("db","Userknowledgepoint",bson.M{"courseid":in.Courseid,"userid":in.Userid},bson.M{"syllabusknowledgepointid":1,"isright":1},&errpoints)
	for _,a:= range errpoints{
		pointall[a.SyllabusKnowledgePointID]= point{id:a.SyllabusKnowledgePointID,state:a.IsRight}
		if a.IsRight==1{
			r= append(r,a.SyllabusKnowledgePointID )
		}
		if a.IsRight==2{
			w= append(w,a.SyllabusKnowledgePointID )
		}
	}
	var newpoints=[]Model.Checkknowledgepoint{}
	err:=db.FindAll("db","Checkknowledgepoint",bson.M{"createtestpaperid":in.CreateTestpaperID},bson.M{"syllabusknowledgepointid":1},&newpoints)
	if(err!=nil){

	}
	for _,a:= range newpoints{
		pointall[a.SyllabusKnowledgePointID]= point{id:a.SyllabusKnowledgePointID,state:3}
		n=append(n,a.SyllabusKnowledgePointID )
	}
	var poinst=pointsall{w,r,n}
	//pointall=removeDuplicatesAndEmpty(pointall)
	//开始拿题
	allquestion:=[]Model.Questions{}
	ques:=ques{}

	var a=poinst.new
	a= append(a, poinst.err...)
	a= append(a, poinst.rev...)
	//拿到所有题

	db.FindAll("db","Questions",bson.M{"syllabusknowledgepointid":bson.M{"$in":a}},bson.M{"addtime":0,"adduserid":0,"answers.adduserid":0,"title":0,"answers.addtime":0,"answers.isdecompression":0,"answers.questionsid":0,"answers.realanswer":0},&allquestion)
	//for _,a:= range allquestion{
	//	pointall=append(pointall, point{id:a.SyllabusKnowledgePointID,state:3})
	//}
	//fmt.Printf(",共找到试题:%+v", allquestion[0])
	//fmt.Printf("共获取到知识点数量:%d\n,共找到试题数量:%+v\n", len(pointall), len(allquestion))
	num:=<-numcha
	if len(allquestion)<num.all{
		wg.Done()
		fmt.Printf("试题数量不够（所选知识点涉及总试题数量为）"+strconv.Itoa(len(allquestion))+"，小于所需题目数量"+strconv.Itoa(num.all))
		goCount--
		return
		//return &pb.CreateTestpaperReply{Question:nil,Error:true,ErrorMsg:"试题数量不够（所选知识点涉及总试题数量为）"+strconv.Itoa(len(allquestion))+"，小于所需题目数量"+strconv.Itoa(num.all)},nil
	}
	//var cha=make(chan int)
	//i:=0
	for _,v :=range allquestion{
		  sxQuestion(v,&pointall,&ques)
		//<-cha
	}
	//for v :=range cha {
	//	i+=v
	//	if i>= len(allquestion){
	//		close(cha);
	//	break;
	//	}
	//}
	//fmt.Printf("序列化后试题数量回顾简单:%d\n", len(ques.review_easy))
	//fmt.Printf("序列化后试题数量回顾中单:%d\n", len(ques.review_middle))
	//fmt.Printf("序列化后试题数量回顾困难:%d\n", len(ques.review_diff))
	//fmt.Printf("序列化后试题数量错题简单:%d\n", len(ques.worong_easy))
	//fmt.Printf("序列化后试题数量错题中单:%d\n", len(ques.worong_middle))
	//fmt.Printf("序列化后试题数量错题困难:%d\n", len(ques.worong_diff))
	//fmt.Printf("序列化后试题数量新知识简单:%d\n", len(ques.new_easy))
	//fmt.Printf("序列化后试题数量新知识中单:%d\n", len(ques.new_middle))
	//fmt.Printf("序列化后试题数量新知识困难:%d\n", len(ques.new_diff))

	//现在得到了九个类型的所有题，开始根据规则拼成试卷,ques:试题,num:规则
	var testpaper=[]Model.Questions{}
	var beitestpaper=[]Model.Questions{}
	 chaquestion:=0
	//fmt.Printf("测试测试测试:%v\n%d\n%d\n%d\n%d\n", testpaper,len(testpaper),num.review_diff,num.review_middle,num.review_easy)
	//----------------------------------1-----------------------------------------------
	if num.review_diff> len(ques.review_diff){
		testpaper=append(testpaper,ques.review_diff... )
		num.review_middle+=num.review_diff- len(ques.review_diff)
	}else {
		testpaper=append(testpaper,ques.review_diff[0:num.review_diff]... )
		if num.review_diff> len(ques.review_diff){
			beitestpaper=append(beitestpaper,ques.review_diff[num.review_diff:len(ques.review_diff)]... )
		}
	}
	//----------------------------------2-----------------------------------------------
	if num.review_middle> len(ques.review_middle){
		testpaper=append(testpaper,ques.review_middle... )
		num.review_easy+=num.review_middle- len(ques.review_middle)
	}else {
		testpaper=append(testpaper,ques.review_middle[0:num.review_middle]... )
		if num.review_middle> len(ques.review_middle){
			beitestpaper=append(beitestpaper,ques.review_middle[num.review_middle:len(ques.review_middle)]... )
		}
	}
	//-----------------------------------3----------------------------------------------
	if num.review_easy> len(ques.review_easy){
		testpaper=append(testpaper,ques.review_easy... )
		num.worong_easy+=num.review_easy- len(ques.review_easy)
	}else {
		testpaper=append(testpaper,ques.review_easy[0:num.review_easy]... )
		if num.review_easy> len(ques.review_easy){
			beitestpaper=append(beitestpaper,ques.review_easy[num.review_easy:len(ques.review_easy)]... )
		}
	}
	//------------------------------------4---------------------------------------------
	if num.worong_diff> len(ques.worong_diff){
		testpaper=append(testpaper,ques.worong_diff... )
		num.worong_middle+=num.worong_diff- len(ques.worong_diff)
	}else {
		testpaper=append(testpaper,ques.worong_diff[0:num.worong_diff]... )
		if num.worong_diff> len(ques.worong_diff){
			beitestpaper=append(beitestpaper,ques.worong_diff[num.worong_diff:len(ques.worong_diff)]... )
		}
	}
	//------------------------------------5---------------------------------------------
	if num.worong_middle> len(ques.worong_middle){
		testpaper=append(testpaper,ques.worong_middle... )
		num.worong_easy+=num.worong_middle- len(ques.worong_middle)
	}else {
		testpaper=append(testpaper,ques.worong_middle[0:num.worong_middle]... )
		if num.worong_middle> len(ques.worong_middle){
			beitestpaper=append(beitestpaper,ques.worong_middle[num.worong_middle:len(ques.worong_middle)]... )
		}
	}
	//-------------------------------------6--------------------------------------------
	if num.worong_easy> len(ques.worong_easy){
		testpaper=append(testpaper,ques.worong_easy... )
		num.new_middle+=num.worong_easy- len(ques.worong_easy)
	}else {
		testpaper=append(testpaper,ques.worong_easy[0:num.worong_easy]... )
		if num.worong_easy> len(ques.worong_easy){
			beitestpaper=append(beitestpaper,ques.worong_easy[num.worong_easy:len(ques.worong_easy)]... )
		}
	}
	//-------------------------------------7--------------------------------------------
	if num.new_diff> len(ques.new_diff){
		testpaper=append(testpaper,ques.new_diff... )
		num.new_middle+=num.new_diff- len(ques.new_diff)
	}else {
		testpaper=append(testpaper,ques.new_diff[0:num.new_diff]... )
		if num.new_diff> len(ques.new_diff){
			beitestpaper=append(beitestpaper,ques.new_diff[num.new_diff:len(ques.new_diff)]... )
		}
	}
	//-------------------------------------8--------------------------------------------
	if num.new_middle> len(ques.new_middle){
		testpaper=append(testpaper,ques.new_middle... )
		num.new_easy+=num.new_middle- len(ques.new_middle)
	}else {
		testpaper=append(testpaper,ques.new_middle[0:num.new_middle]... )
		if num.new_middle> len(ques.new_middle){
			beitestpaper=append(beitestpaper,ques.new_middle[num.new_middle:len(ques.new_middle)]... )
		}
	}
	//-------------------------------------9--------------------------------------------
	if num.new_easy> len(ques.new_easy){
		testpaper=append(testpaper,ques.new_easy... )
		chaquestion=num.new_easy- len(ques.new_easy)
	}else {
		testpaper=append(testpaper,ques.new_easy[0:num.new_easy]... )

	}
	//---------------------------------10------------------------------------------------
	if chaquestion!=0{
		//fmt.Printf("总数量%d,要截取数量%d",len(beitestpaper),chaquestion)
		if(len(beitestpaper)<chaquestion){
			wg.Done()
			goCount--
			return
		}
		testpaper=append(testpaper, beitestpaper[0:chaquestion]...)
	}
	 //---------------------------------生成试卷完成------------------------------------
	//fmt.Printf("测试测试测试:%v\n试题数量%d\n%d\n%d\n%d\n", testpaper,len(testpaper),num.review_diff,num.review_middle,num.review_easy)
	var tcha=make(chan *pb.Questions)
	for _,v:=range testpaper {
		go comm.Quuestion(v,tcha)
		ret= append(ret, <-tcha)
	}
	close(tcha)
	//for q:=range tcha{
	//	ret= append(ret, q)
	//	if len(ret)>= len(testpaper){
	//		fmt.Printf("测试:%d\n",len(ret))
	//		fmt.Printf("测试:%d\n",len(testpaper))
	//		close(tcha)
	//		break
	//	}
	//}
	//var t2 =time.Now().UnixNano()/1e6
	//  fmt.Printf("回顾简单:%d\n", num.review_easy)
	//  fmt.Printf("回顾中单:%d\n", num.review_middle)
	//  fmt.Printf("回顾困难:%d\n", num.review_diff)
	//  fmt.Printf("错题简单:%d\n", num.worong_easy)
	//  fmt.Printf("错题中单:%d\n", num.worong_middle)
	//  fmt.Printf("错题困难:%d\n", num.worong_diff)
	//fmt.Printf("新知识简单:%d\n", num.new_easy)
	//fmt.Printf("新知识中单:%d\n", num.new_middle)
	//fmt.Printf("新知识困难:%d\n", num.new_diff)
	//fmt.Printf("总数量:%d\n", len(ret))
	//fmt.Printf("总数量:%d\n", len(testpaper))
	//fmt.Printf("用时:%d,,\n",t2-t)
	var dat=modl.TestpaperQueue{}
	dat.Addtime=time.Now().UnixNano()/1e6
	dat.Questions=ret
	dat.State=1
	dat.Userid=in.Userid
	dat.CreateTestpaperID=in.CreateTestpaperID
	dat.Courseid=in.Courseid
	db.Update("db","TestpaperQueue",bson.M{"userid":in.Userid},dat)
	wg.Done()
	goCount--
	return
	//return &pb.CreateTestpaperReply{Question:ret,Error:false,ErrorMsg:""},nil
}
func getQuestionNum(r *pb.Role,cha chan quesNum)(){
	var num =quesNum{}
	//记录总题数量
	rnum:=r.Middle+r.Easy+r.Diff
	//fmt.Printf("总数:%d\n",rnum)
	//获得困难及中等所占比例，剩下的为简单题
	r3:=float32(int(r.Diff*100/rnum))*0.01
	r2:=float32(int(r.Middle*100/rnum))*0.01
	//fmt.Printf("r3,r2:%d---%d\n",r3,r2)
	//计算错题三个等级所需数量
	num.worong_diff=int(float32(r.Wrongnum)*r3)
	num.worong_middle=int(float32(r.Wrongnum)*r2)
	num.worong_easy=int(r.Wrongnum)-num.worong_diff-num.worong_middle
	//fmt.Printf("w1,w2,w3:%d---%d---%d\n",num.worong_diff,num.worong_middle,num.worong_easy)
	//回顾题
	num.review_diff=int(float32(r.Reviewnum)*r3)
	num.review_middle=int(float32(r.Reviewnum)*r2)
	num.review_easy=int(r.Reviewnum)-num.review_diff-num.review_middle
	//新知识
	num.new_diff=int(float32(r.Newnum)*r3)
	num.new_middle=int(float32(r.Newnum)*r2)
	num.new_easy=int(r.Newnum)-num.new_diff-num.new_middle
	//fmt.Printf("测试:%+v\n",num)
	cha<-num
}
func sxQuestion(v Model.Questions,pointall *map[string]point,ques *ques)  {
	switch v.Difficulty {
	case 1:
		switch (*pointall)[v.SyllabusKnowledgePointID].state {
		case 1://简单回顾
			ques.review_easy=append(ques.review_easy, v)
			break;
		case 2://简单错题
		//fmt.Printf("测试%+v",ques.worong_easy)
			ques.worong_easy=append(ques.worong_easy, v)
			break;
		case 3://简单新知识
			ques.new_easy=append(ques.new_easy, v)
			break;
		default:
			break
		}
		break;
	case 2:
		switch (*pointall)[v.SyllabusKnowledgePointID].state {
		case 1://中等回顾
			ques.review_middle=append(ques.review_middle, v)
			break;
		case 2://中等错题
			ques.worong_middle=append(ques.worong_middle, v)
			break;
		case 3://中等新知识
			ques.new_middle=append(ques.new_middle, v)
			break;
		default:
			break
		}
		break;
	case 3:
		switch (*pointall)[v.SyllabusKnowledgePointID].state {
		case 1://困难回顾
			ques.review_diff=append(ques.review_diff, v)
			break;
		case 2://困难错题
			ques.worong_diff=append(ques.worong_diff, v)
			break;
		case 3://困难新知识
			ques.new_diff=append(ques.new_diff, v)
			break;
		default:
			break
		}
		break;
	default:
		break
	}

}
////分组去重
//
//func removeDuplicatesAndEmpty(a []point) (ret []point){
//	sort.Strings(a)
//	a_len := len(a)
//	for i:=0; i < a_len; i++{
//		if (i > 0 && a[i-1] == a[i]) || len(a[i])==0{
//			continue;
//		}
//		ret = append(ret, a[i])
//	}
//	return
//}


